#include "prmt_mngmtserver.h"
#include "cwmp_notify.h"
#include "cwmp_rpc.h"
#include "udp.h"
#include "stun.h"

#define CWMPPID  "/var/run/cwmp.pid"

#ifdef TR069_ANNEX_G
#define	STUN_BINDING_REQ				1
#define STUN_BINDING_MAINTAIN			2
#define STUN_BINDING_TIMEOUT_DISCOVERY	3
#define STUN_BINDING_TIMEOUT_MONITORING	4
#define STUN_BINDING_CHANGE				5
#define STUN_CHANGE_OTHER_PARAM			6
	
#define CWMP_STUN_CNONCE_LEN			256
#define CWMP_STUN_SIGNATURE_LEN			40
#define CWMP_STUN_SIGNATURE_STRING_LEN	2048
	
static int stunThread = 0;
static int stunStop = 0;
static int stunState = STUN_BINDING_REQ;
static int OldStunState = STUN_BINDING_REQ;
	
static pthread_cond_t cond;
static pthread_mutex_t mutex;
	
static char udpConnReqAddr[CWMP_UDP_CONN_REQ_ADDR_LEN+1];
static char stunServerAddr[CWMP_STUN_SERVER_ADDR_LEN+1];
static char OldStunServerAddrAndPort[CWMP_STUN_SERVER_ADDR_LEN+1];
static unsigned int stunServerPort;
static char stunUsername[CWMP_STUN_USERNAME_LEN+1];
static char stunPassword[CWMP_STUN_PASSWORD_LEN+1];
static int stunMaxPeriod;
static unsigned int stunMinPeriod;
static unsigned int natDetected;
static unsigned int msgIntegrityDetected;
//static unsigned int msgIntegrityDetected=1;
	
static char acsUrl[CWMP_ACS_URL_LEN+1];
	
static long long g_ts = 0;
static unsigned int g_id = 0;
#endif

/*star:20091228 START add for store parameterkey*/
//char gParameterKey[32+1];
/*star:20091228 END*/
char gConnectionRequestURL[256+1];

char reInformIntervalFlag = 0;

#ifdef _TR_111_PRMT_
int gDeviceNumber=0;

/****ManageDevEntity*******************************************************************************************/
struct CWMP_OP tManageDevEntityLeafOP = { getManageDevEntity, NULL };
struct CWMP_PRMT tManageDevEntityLeafInfo[] =
{
/*(name,		type,		flag,		op)*/
{"ManufacturerOUI",	eCWMP_tSTRING,	CWMP_READ,	&tManageDevEntityLeafOP},
{"SerialNumber",	eCWMP_tSTRING,	CWMP_READ,	&tManageDevEntityLeafOP},
{"ProductClass",	eCWMP_tSTRING,	CWMP_READ,	&tManageDevEntityLeafOP},
/*ping_zhang:20081217 START:patch from telefonica branch to support WT-107*/
#ifdef _PRMT_WT107_
{"Host",	eCWMP_tSTRING,	CWMP_READ,	&tManageDevEntityLeafOP},
#endif
/*ping_zhang:20081217 END*/
};
enum eManageDevEntityLeaf
{
	eMDManufacturerOUI,
	eMDSerialNumber,
	eMDProductClass,
/*ping_zhang:20081217 START:patch from telefonica branch to support WT-107*/
#ifdef _PRMT_WT107_
	eMDHost
#endif
/*ping_zhang:20081217 END*/
};
struct CWMP_LEAF tManageDevEntityLeaf[] =
{
{ &tManageDevEntityLeafInfo[eMDManufacturerOUI] },
{ &tManageDevEntityLeafInfo[eMDSerialNumber] },
{ &tManageDevEntityLeafInfo[eMDProductClass] },
/*ping_zhang:20081217 START:patch from telefonica branch to support WT-107*/
#ifdef _PRMT_WT107_
{ &tManageDevEntityLeafInfo[eMDHost] },
#endif
/*ping_zhang:20081217 END*/
{ NULL }
};

/****ManageableDevice*******************************************************************************************/
struct CWMP_PRMT tManageableDeviceOjbectInfo[] =
{
/*(name,	type,		flag,			op)*/
{"0",		eCWMP_tOBJECT,	CWMP_READ|CWMP_LNKLIST,	NULL}
};
enum eManageableDeviceOjbect
{
	eMD0
};
struct CWMP_LINKNODE tManageableDeviceObject[] =
{
/*info,  				leaf,			next,		sibling,		instnum)*/
{&tManageableDeviceOjbectInfo[eMD0],tManageDevEntityLeaf,	NULL,		NULL,			0},
};
/***********************************************************************************************/
#endif

/*******ManagementServer****************************************************************************************/
struct CWMP_OP tManagementServerLeafOP = { getMngmntServer,setMngmntServer };
struct CWMP_PRMT tManagementServerLeafInfo[] =
{
/*(name,				type,		flag,			op)*/
{"EnableCWMP",			eCWMP_tBOOLEAN,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"URL",					eCWMP_tSTRING,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"Username",				eCWMP_tSTRING,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"Password",				eCWMP_tSTRING,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"PeriodicInformEnable",		eCWMP_tBOOLEAN,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"PeriodicInformInterval",		eCWMP_tUINT,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"PeriodicInformTime",			eCWMP_tDATETIME,CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"ParameterKey",			eCWMP_tSTRING,	CWMP_READ|CWMP_DENY_ACT,&tManagementServerLeafOP},
{"ConnectionRequestURL",		eCWMP_tSTRING,	CWMP_READ,		&tManagementServerLeafOP},
{"ConnectionRequestUsername",		eCWMP_tSTRING,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"ConnectionRequestPassword",		eCWMP_tSTRING,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"UpgradesManaged",			eCWMP_tBOOLEAN,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"KickURL",				eCWMP_tSTRING,	CWMP_READ,		&tManagementServerLeafOP},
{"DownloadProgressURL",			eCWMP_tSTRING,	CWMP_READ,		&tManagementServerLeafOP},
{"DefaultActiveNotificationThrottle",	eCWMP_tUINT,	CWMP_READ|CWMP_WRITE,		&tManagementServerLeafOP},
#ifdef TR069_ANNEX_G
{"UDPConnectionRequestAddress", 	  eCWMP_tSTRING,	CWMP_READ,	&tManagementServerLeafOP},
{"STUNEnable",						  eCWMP_tBOOLEAN,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"STUNServerAddress",				  eCWMP_tSTRING,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"STUNServerPort",					  eCWMP_tUINT,		CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"STUNUsername",					  eCWMP_tSTRING,	CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"STUNPassword",					  eCWMP_tSTRING,	CWMP_PASSWORD|CWMP_WRITE|CWMP_READ, &tManagementServerLeafOP},
{"STUNMaximumKeepAlivePeriod",		  eCWMP_tINT,		CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"STUNMinimumKeepAlivePeriod",		  eCWMP_tUINT,		CWMP_WRITE|CWMP_READ,	&tManagementServerLeafOP},
{"NATDetected", 					  eCWMP_tBOOLEAN,	CWMP_READ,	&tManagementServerLeafOP},
#endif
#ifdef _TR_111_PRMT_
{"ManageableDeviceNumberOfEntries",	eCWMP_tUINT,	CWMP_READ,		&tManagementServerLeafOP},
{"ManageableDeviceNotificationLimit",	eCWMP_tUINT,	CWMP_READ|CWMP_WRITE,		&tManagementServerLeafOP},
#endif
};
enum eManagementServerLeaf
{
	eMSEnableCWMP,
	eMSURL,
	eMSUsername,
	eMSPassword,
	eMSPeriodicInformEnable,
	eMSPeriodicInformInterval,
	eMSPeriodicInformTime,
	eMSParameterKey,
	eMSConnectionRequestURL,
	eMSConnectionRequestUsername,
	eMSConnectionRequestPassword,
	eMSUpgradesManaged,
	eMSKickURL,
	eMSDownloadProgressURL,
	eMSDefaultActiveNotificationThrottle,
#ifdef TR069_ANNEX_G
	eMS_UDPConnectionRequestAddress,
	eMS_STUNEnable,
	eMS_STUNServerAddress,
	eMS_STUNServerPort,
	eMS_STUNUsername,
	eMS_STUNPassword,
	eMS_STUNMaximumKeepAlivePeriod,
	eMS_STUNMinimumKeepAlivePeriod,
	eMS_NATDetected,
#endif
#ifdef _TR_111_PRMT_
	eMSManageableDeviceNumberOfEntries,
	eMSManageableDeviceNotificationLimit,
#endif
};
struct CWMP_LEAF tManagementServerLeaf[] =
{
{ &tManagementServerLeafInfo[eMSEnableCWMP] },
{ &tManagementServerLeafInfo[eMSURL] },
{ &tManagementServerLeafInfo[eMSUsername] },
{ &tManagementServerLeafInfo[eMSPassword] },
{ &tManagementServerLeafInfo[eMSPeriodicInformEnable] },
{ &tManagementServerLeafInfo[eMSPeriodicInformInterval] },
{ &tManagementServerLeafInfo[eMSPeriodicInformTime] },
{ &tManagementServerLeafInfo[eMSParameterKey] },
{ &tManagementServerLeafInfo[eMSConnectionRequestURL] },
{ &tManagementServerLeafInfo[eMSConnectionRequestUsername] },
{ &tManagementServerLeafInfo[eMSConnectionRequestPassword] },
{ &tManagementServerLeafInfo[eMSUpgradesManaged] },
{ &tManagementServerLeafInfo[eMSKickURL] },
{ &tManagementServerLeafInfo[eMSDownloadProgressURL] },
{ &tManagementServerLeafInfo[eMSDefaultActiveNotificationThrottle] },
#ifdef TR069_ANNEX_G
{ &tManagementServerLeafInfo[eMS_UDPConnectionRequestAddress] },
{ &tManagementServerLeafInfo[eMS_STUNEnable] },
{ &tManagementServerLeafInfo[eMS_STUNServerAddress] },
{ &tManagementServerLeafInfo[eMS_STUNServerPort] },
{ &tManagementServerLeafInfo[eMS_STUNUsername] },
{ &tManagementServerLeafInfo[eMS_STUNPassword] },
{ &tManagementServerLeafInfo[eMS_STUNMaximumKeepAlivePeriod] },
{ &tManagementServerLeafInfo[eMS_STUNMinimumKeepAlivePeriod] },
{ &tManagementServerLeafInfo[eMS_NATDetected] },
#endif
#ifdef _TR_111_PRMT_
{ &tManagementServerLeafInfo[eMSManageableDeviceNumberOfEntries] },
{ &tManagementServerLeafInfo[eMSManageableDeviceNotificationLimit] },
#endif
{ NULL	}
};
#ifdef _TR_111_PRMT_
struct CWMP_OP tMS_ManageableDevice_OP = { NULL, objManageDevice };
struct CWMP_PRMT tManagementServerObjectInfo[] =
{
/*(name,				type,		flag,			op)*/
{"ManageableDevice",			eCWMP_tOBJECT,	CWMP_READ,		&tMS_ManageableDevice_OP}
};
enum eManagementServerObject
{
	eMSManageableDevice
};
struct CWMP_NODE tManagementServerObject[] =
{
/*info,  						leaf,		next)*/
{&tManagementServerObjectInfo[eMSManageableDevice],	NULL,		NULL},
{NULL,							NULL,		NULL}
};
#endif

#define CONFIG_SET(key, val) if ( mib_set(key, val)==0)  return ERR_9002
#define CONFIG_GET(key, ret) if ( mib_get(key, ret)==0)  return ERR_9002
#define CHECK_PARAM_STR(str, min, max)  do { \
	int tmp; \
	if (!str) return ERR_9007; \
	tmp=strlen(str); \
	if ((tmp < min) || (tmp > max)) return ERR_9007; \
}	while (0)

#define CHECK_PARAM_NUM(input, min, max) if ( (input < min) || (input > max) ) return ERR_9007;

enum {
	EN_ENABLE_CWMP = 0,
	EN_URL,
	EN_USERNAME, 
	EN_PASSWORD,
	EN_PERIODIC_INFORM_ENABLE,
	EN_PERIODIC_INTERVAL,
	EN_PERIODIC_TIME,
	EN_PARAMETER_KEY,
	EN_CONNREQ_URL,
	EN_CONNREQ_USERNAME,
	EN_CONNREQ_PASSWORD,
	EN_UPGRADE_MANAGED,
	EN_KICKURL,
	EN_DOWNLOADURL,
	EN_DEFAULT_ACTIVE_NORTIFICATION_THROTTLE,
#ifdef TR069_ANNEX_G
	EN_UDPConnectionRequestAddress,
	EN_STUNEnable,
	EN_STUNServerAddress,
	EN_STUNServerPort,
	EN_STUNUsername,
	EN_STUNPassword,
	EN_STUNMaximumKeepAlivePeriod,
	EN_STUNMinimumKeepAlivePeriod,
	EN_NATDetected,
#endif
#ifdef _TR_111_PRMT_
	EN_MANAGEABLEDEVICENUMBER,
	EN_MANAGEABLE_DEVICE_NORTIFICATION_LIMIT,
#endif
};

/*******************************************************************************
Function
*******************************************************************************/
#ifdef TR069_ANNEX_G
static void procUdpConnReq(char *buf, unsigned int bufLen)
{
	int i;
	char *p;
	long long ts = 0; // timestamp
	unsigned int id = 0;
	char un[CWMP_STUN_USERNAME_LEN+1]; // username
	char cn[CWMP_STUN_CNONCE_LEN+1]; // Cnonce
	char sig[CWMP_STUN_SIGNATURE_LEN+1]; // Signature

	char sigStr[CWMP_STUN_SIGNATURE_STRING_LEN];
	char hash[20];
	char hashStr[CWMP_STUN_SIGNATURE_LEN+1];
	
	char connReqUN[CWMP_CONREQ_USERNAME_LEN+1];
	char connReqPW[CWMP_CONREQ_PASSWD_LEN+1];

	tr098_printf("UDP Conn Req: %s", buf);
	tr098_printf("Len (%d)", bufLen);

#if 1  // validation and authentication

	if (buf == NULL)
	{
		tr098_printf("buf NULL error!");
		return;
	}

	if (strncmp(buf, "GET", 3) != 0)
	{
		tr098_printf("not a GET msg!");
		return;
	}

	if (strstr(buf, "HTTP/1.1") == NULL)
	{
		tr098_printf("not a HTTP/1.1 msg!");
		return;
	}

	p = strtok(buf, "?=& ");

	while (p != NULL)
	{
		tr098_printf("strtok (%s)", p);

		if (strcmp(p, "ts") == 0)
		{
			p = strtok(NULL, "?=& ");

			if (p != NULL)
			{
				ts = atoll(p);
				tr098_printf("ts (%lld)", ts);
			}
		}
		else if (strcmp(p, "id") == 0)
		{
			p = strtok(NULL, "?=& ");

			if (p != NULL)
			{
				id = atoi(p);
				tr098_printf("id (%u)", id);
			}
		}
		else if (strcmp(p, "un") == 0)
		{
			p = strtok(NULL, "?=& ");

			if (p != NULL)
			{
				strcpy(un, p);
				tr098_printf("un (%s)", un);
			}
		}
		else if (strcmp(p, "cn") == 0)
		{
			p = strtok(NULL, "?=& ");

			if (p != NULL)
			{
				strcpy(cn, p);
				tr098_printf("cn (%s)", cn);
			}
		}
		else if (strcmp(p, "sig") == 0)
		{
			p = strtok(NULL, "?=& ");

			if (p != NULL)
			{
				strcpy(sig, p);
				tr098_printf("sig (%s)", sig);
			}
		}

		p = strtok(NULL, "?=& ");
	}

	if (ts <= g_ts)
	{
		tr098_printf("ts(%lld) <= g_ts(%lld) error!", ts, g_ts);
		return;
	}
	else
	{
		g_ts = ts;
	}

	if (id == g_id)
	{
		tr098_printf("id(%u) == g_id(%u) error!", id, g_id);
		return;
	}
	else
	{
		g_id = id;
	}

	mib_get(MIB_CWMP_CONREQ_USERNAME, connReqUN);

	if (strcmp(un, connReqUN) != 0)
	{
		tr098_printf("username (un) mismatch error!");
		return;
	}

	mib_get(MIB_CWMP_CONREQ_PASSWORD, connReqPW);

	sprintf(sigStr, "%lld%u%s%s", ts, id, un, cn);

	tr098_printf("sigStr (%s)", sigStr);

    /* use sigStr with connReqPW to generate Hmac for comparison. */
#ifdef HAS_MESSAGE_INTEGRITY
	computeHmac(hash, sigStr, strlen(sigStr), connReqPW, strlen(connReqPW));
	toHex(hash, 20, hashStr);
	hashStr[CWMP_STUN_SIGNATURE_LEN] = 0;
	if(strncasecmp(sig, hashStr, CWMP_STUN_SIGNATURE_LEN) !=0 )
	{
		tr098_printf("signature (sig) mismatch error!, server sig(%s), client sig(%s)",sig, hashStr);
		return;
	}
#else
#error "please set openssl-0.9.8i!"
#endif

#endif

	cwmpSendUdpConnReq();
}

static void stun_getHost(char *host, char *url)
{ 
	const char *s;
	int i, n;

	if (!url || !*url)
		return;

	s = strchr(url, ':');
	if (s && s[1] == '/' && s[2] == '/')
		s += 3;
	else
		s = url;

	tr098_printf("s (%s)", s);

	n = strlen(s);

	for (i = 0; i < n; i++)
	{ 
		host[i] = s[i];
		if (s[i] == '/' || s[i] == ':')
			break; 
	}

	host[i] = '\0';
	return;
}

static int stun_getHostByName(char *addr, struct in_addr *inaddr)
{ 
	in_addr_t iadd = -1;
	struct hostent hostent, *host = &hostent;

	iadd = inet_addr(addr);

	if (iadd != -1)
	{ 
		memcpy(inaddr, &iadd, sizeof(iadd));
		return 0;
	}

	host = gethostbyname(addr);

	if (!host)
	{ 
		tr098_printf("Host name not found");
		return -1;
	}

	memcpy(inaddr, host->h_addr, host->h_length);

	return 0;
}

static void *stun_thread(void *arg)
{
	StunAddress4 stunServer, stunMappedAddr;
	StunMessage stunMsg;
	Socket priFd = -1, secFd = -1, s = -1;
	struct in_addr in;
	char checkAddr[CWMP_UDP_CONN_REQ_ADDR_LEN+1];
	char checkStunServerAddrAndPort[CWMP_UDP_CONN_REQ_ADDR_LEN+1];
	char host[CWMP_ACS_URL_LEN+1];
	unsigned int checkNat;
	unsigned int stunPeriod;
	struct timeval now;
	struct timespec outtime;
	time_t now_time_t, out_time_t;

	tr098_printf("stun started!!!");
	//sleep(1); //wait for set value to mib

	pthread_mutex_lock(&mutex);

stun_reread:
	stunState = STUN_BINDING_REQ;
	memset(stunUsername,0,CWMP_STUN_USERNAME_LEN+1);
	memset(stunPassword,0,CWMP_STUN_PASSWORD_LEN+1);
	mib_get(MIB_CWMP_STUN_SERVER_ADDR, stunServerAddr);
	mib_get(MIB_CWMP_STUN_SERVER_PORT, &stunServerPort);
	mib_get(MIB_CWMP_STUN_USERNAME, stunUsername);
	mib_get(MIB_CWMP_STUN_PASSWORD, stunPassword);
	mib_get(MIB_CWMP_STUN_MAX_KEEP_ALIVE_PERIOD, &stunMaxPeriod);
	mib_get(MIB_CWMP_STUN_MIN_KEEP_ALIVE_PERIOD, &stunMinPeriod);

	mib_get(MIB_CWMP_ACS_URL, acsUrl);

	tr098_printf("stunServerAddr (%s)", stunServerAddr);
	tr098_printf("stunServerPort (%d)", stunServerPort);
	tr098_printf("stunUsername (%s)", stunUsername);
	tr098_printf("stunPassword (%s)", stunPassword);
	tr098_printf("stunMaxPeriod (%d)", stunMaxPeriod);
	tr098_printf("stunMinPeriod (%d)", stunMinPeriod);
	tr098_printf("acsUrl (%s)", acsUrl);

	memset(&stunServer, 0, sizeof(StunAddress4));

	if (strlen(stunServerAddr) == 0)
	{
		stun_getHost(host, acsUrl);
	}
	else
	{
		stun_getHost(host, stunServerAddr);
	}

	tr098_printf("host %s", host);

	while(1)
	{
		if (stun_getHostByName(host, &in) == -1)
			sleep(5);
		else
			break;
		//goto stun_err;
	}
	
	stunServer.addr = (UInt32)in.s_addr;

	if (stunServerPort == 0)
	{
		stunServer.port = STUN_PORT; // use default stun port number
	}
	else
	{
		stunServer.port = stunServerPort;
	}

	//in.s_addr = htonl(stunServer.addr);
	in.s_addr = stunServer.addr;
	tr098_printf("stun server: %s:%d", inet_ntoa(in), stunServer.port);
	sprintf(OldStunServerAddrAndPort, "%s:%d", inet_ntoa(in), stunServer.port);

	while (!stunStop)
	{
		tr098_printf("stunState (%d), msgIntegrityDetected (%d)", stunState, msgIntegrityDetected);

		memset(&stunMsg, 0, sizeof(StunMessage));
		
		switch (stunState)
		{
			case STUN_BINDING_REQ:
				tr098_trace();

				priFd = openPort(0, 0);

				stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 0, 0, NULL, stunUsername, NULL, procUdpConnReq);
				//stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
				if (stunMsg.hasErrorCode && stunMsg.errorCode.errorClass == 4 &&
							stunMsg.errorCode.number == 1) // Unauthorized
				{
					msgIntegrityDetected = 1;
					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);

					if (stunMsg.hasErrorCode)
					{
						tr098_printf("Unauthorized Username/Password");
						close(priFd);
						priFd = -1;
						break;
					}
				}
				else
				{
					msgIntegrityDetected = 0;
				}
				tr098_printf("msgIntegrityDetected = %u", msgIntegrityDetected);

				if (stunMsg.hasMappedAddress)
				{
					s = openPort(11000, stunMsg.mappedAddress.ipv4.addr);

					if (s != INVALID_SOCKET) // not behind NAT
					{
						close(s);
						s = -1;
						natDetected = 0;
					}
					else
					{
						natDetected = 1;
					}

					stunMappedAddr.addr = stunMsg.mappedAddress.ipv4.addr;
					stunMappedAddr.port = stunMsg.mappedAddress.ipv4.port;

					in.s_addr = htonl(stunMsg.mappedAddress.ipv4.addr);
					sprintf(udpConnReqAddr, "%s:%d", inet_ntoa(in),
						stunMsg.mappedAddress.ipv4.port);

					mib_set(MIB_CWMP_UDP_CONN_REQ_ADDR, udpConnReqAddr);
					mib_set(MIB_CWMP_NAT_DETECTED, &natDetected);

					tr098_printf("isNAT: %d, UDPConnReqAddr: %s", natDetected, udpConnReqAddr);

					if (natDetected)
					{
						/* Binding Req with BindingChange attr. */
						if(msgIntegrityDetected == 1)
							stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
						else
							stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, NULL, procUdpConnReq);

#ifdef _CWMP_WITH_SSL_
						if (stunMsg.hasErrorCode && stunMsg.errorCode.errorClass == 4 &&
							stunMsg.errorCode.number == 1) // Unauthorized
						{
							msgIntegrityDetected = 1;
							stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);

							if (stunMsg.hasErrorCode)
							{
								tr098_printf("Unauthorized Username/Password");
								close(priFd);
								break;
							}
						}
						else
						{
							//msgIntegrityDetected = 0;
						}
#endif

						if (stunMaxPeriod == stunMinPeriod)
						{
							stunPeriod = stunMinPeriod;
							stunState = STUN_BINDING_MAINTAIN;
						}
						else
						{
							stunPeriod = (stunMinPeriod + stunMaxPeriod) / 2;
							if(stunPeriod == stunMinPeriod || stunPeriod == stunMaxPeriod)
								stunState = STUN_BINDING_MAINTAIN;
							else
								stunState = STUN_BINDING_TIMEOUT_DISCOVERY;
						}

						tr098_printf("stunState (%d) stunPeriod (%d) "
							"stunMinPeriod (%d) stunMaxPeriod (%d)",
							stunState, stunPeriod, stunMinPeriod, stunMaxPeriod);
					}
					else
					{
						tr098_trace();
						close(priFd);
						priFd = -1;
						gettimeofday(&now, NULL);
						outtime.tv_sec = now.tv_sec + stunMaxPeriod;
						outtime.tv_nsec = now.tv_usec * 1000;
						pthread_cond_timedwait(&cond, &mutex, &outtime);
						//sleep(stunMaxPeriod);
					}
				}
				else
				{
					tr098_trace();
					close(priFd);
					priFd = -1;
					gettimeofday(&now, NULL);
					//outtime.tv_sec = now.tv_sec + stunMaxPeriod;
					outtime.tv_sec = now.tv_sec + 10;
					outtime.tv_nsec = now.tv_usec * 1000;
					pthread_cond_timedwait(&cond, &mutex, &outtime);
					goto stun_reread;
					//sleep(stunMaxPeriod);
				}
				break;

			case STUN_BINDING_MAINTAIN:
				pthread_mutex_unlock(&mutex);
				now_time_t = time(NULL);
				out_time_t = now_time_t + stunPeriod;
				while(now_time_t < out_time_t)
				{
					tr098_printf("now_time_t = %d, out_time_t = %d\n", now_time_t, out_time_t);
					stunTest_tr111_recv(priFd, 1, &stunMsg, procUdpConnReq);
					if(stunState != STUN_BINDING_MAINTAIN)
					{
						pthread_mutex_lock(&mutex);
						goto stun_binding_maintain_break;
					}
					now_time_t = time(NULL);
					if(out_time_t - now_time_t > stunPeriod) // in case ntp update time to a value much older than last time during while
					{
						tr098_printf("now_time_t = %d, out_time_t = %d, stunPeriod = %d, ntp updated the time, break\n", now_time_t, out_time_t, stunPeriod);
						break;
					}
				}
				pthread_mutex_lock(&mutex);
				//gettimeofday(&now, NULL);
				//outtime.tv_sec = now.tv_sec + stunPeriod;
				//outtime.tv_nsec = now.tv_usec * 1000;
				//pthread_cond_timedwait(&cond, &mutex, &outtime);
				//sleep(stunPeriod);
				
				/* jump to STUN_BINDING_REQ if local IP changed */

				if(msgIntegrityDetected == 1)
					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 0, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
				else
					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 0, 0, NULL, stunUsername, NULL, procUdpConnReq);

				if (stunMsg.hasMappedAddress)
				{
					s = openPort(11000, stunMsg.mappedAddress.ipv4.addr);

					if (s != INVALID_SOCKET) // not behind NAT
					{
						close(s);
						s = -1;
						checkNat = 0;
					}
					else
					{
						checkNat = 1;
					}

					in.s_addr = htonl(stunMsg.mappedAddress.ipv4.addr);
					sprintf(checkAddr, "%s:%d", inet_ntoa(in),
						stunMsg.mappedAddress.ipv4.port);

					tr098_printf("isNAT: %d, UDPConnReqAddr: %s", checkNat, checkAddr);

					if (checkNat != natDetected)
					{
						tr098_printf("natDetected changed!!!");
						mib_set(MIB_CWMP_NAT_DETECTED, &checkNat);
						natDetected = checkNat;
					}

					if (strcmp(checkAddr, udpConnReqAddr) != 0)
					{
						tr098_printf("UDPConnReqAddr changed!!!");
						mib_set(MIB_CWMP_UDP_CONN_REQ_ADDR, checkAddr);
						strcpy(udpConnReqAddr, checkAddr);

						/* do sometheing according to G.2.1.3 */
						if (natDetected)
						{
							/* Binding Req with BindingChange attr. */
							if(msgIntegrityDetected == 1)
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
							else
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, NULL, procUdpConnReq);

#ifdef _CWMP_WITH_SSL_
							if (stunMsg.hasErrorCode && stunMsg.errorCode.errorClass == 4 &&
								stunMsg.errorCode.number == 1) // Unauthorized
							{
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);

								if (stunMsg.hasErrorCode)
								{
									tr098_printf("Unauthorized Username/Password");
									stunState = STUN_BINDING_REQ;
									close(priFd);
									break;
								}
							}
#endif
						}
					}
				}
				else
				{
					tr098_trace();
					stunState = STUN_BINDING_REQ;
					close(priFd);
					priFd = -1;
					gettimeofday(&now, NULL);
					outtime.tv_sec = now.tv_sec + stunMaxPeriod;
					outtime.tv_nsec = now.tv_usec * 1000;
					pthread_cond_timedwait(&cond, &mutex, &outtime);
					//sleep(stunMaxPeriod);
				}
stun_binding_maintain_break:
				break;

			case STUN_BINDING_TIMEOUT_DISCOVERY:
				gettimeofday(&now, NULL);
				outtime.tv_sec = now.tv_sec + stunPeriod;
				outtime.tv_nsec = now.tv_usec * 1000;
				pthread_cond_timedwait(&cond, &mutex, &outtime);
				//sleep(stunPeriod);

				secFd = openPort(stunMappedAddr.port+1, 0);

				if(msgIntegrityDetected == 1)
					stunTest_tr111_send(secFd, &stunServer, 1, 1, 0, 0, 1, &stunMappedAddr, stunUsername, stunPassword);
				else
					stunTest_tr111_send(secFd, &stunServer, 1, 1, 0, 0, 1, &stunMappedAddr, stunUsername, NULL);

				stunTest_tr111_recv(priFd, 1, &stunMsg, procUdpConnReq);

				if (stunMsg.hasMappedAddress) // not timeout yet
				{
					tr098_printf("not timeout yet, stunPeriod (%d)", stunPeriod);
					//stunPeriod *= 2;
					stunMinPeriod = stunPeriod;
					stunPeriod = (stunMinPeriod + stunMaxPeriod)/2;
					if(stunPeriod == stunMinPeriod || stunPeriod == stunMaxPeriod)
						stunState = STUN_BINDING_MAINTAIN;
					tr098_printf("stunPeriod = (%d)", stunPeriod);
				}
				else // already timeout
				{
					tr098_printf("already timeout, stunPeriod (%d)", stunPeriod);
					//stunPeriod /= 2;
					stunMaxPeriod = stunPeriod;
					stunPeriod = (stunMinPeriod + stunMaxPeriod)/2;
					tr098_printf("stunPeriod = (%d)", stunPeriod);
					stunState = STUN_BINDING_TIMEOUT_MONITORING;
				}

				close(secFd);
				secFd = -1;
				break;

			case STUN_BINDING_TIMEOUT_MONITORING:
				if(msgIntegrityDetected == 1)
					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 0, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
				else
					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 0, 0, NULL, stunUsername, NULL, procUdpConnReq);

				if (stunMsg.hasMappedAddress)
				{
					s = openPort(11000, stunMsg.mappedAddress.ipv4.addr);

					if (s != INVALID_SOCKET) // not behind NAT
					{
						close(s);
						s = -1;
						checkNat = 0;
					}
					else
					{
						checkNat = 1;
					}

					stunMappedAddr.addr = stunMsg.mappedAddress.ipv4.addr;
					stunMappedAddr.port = stunMsg.mappedAddress.ipv4.port;

					in.s_addr = htonl(stunMsg.mappedAddress.ipv4.addr);
					sprintf(checkAddr, "%s:%d", inet_ntoa(in),
						stunMsg.mappedAddress.ipv4.port);

					tr098_printf("isNAT: %d, UDPConnReqAddr: %s", checkNat, checkAddr);

					if (checkNat != natDetected)
					{
						tr098_printf("natDetected changed!!!");
						mib_set(MIB_CWMP_NAT_DETECTED, &checkNat);
						natDetected = checkNat;
					}

					if (strcmp(checkAddr, udpConnReqAddr) != 0)
					{
						tr098_printf("UDPConnReqAddr changed!!!");
						mib_set(MIB_CWMP_UDP_CONN_REQ_ADDR, checkAddr);
						strcpy(udpConnReqAddr, checkAddr);

						/* do sometheing according to G.2.1.3 */
						if (natDetected)
						{
							/* Binding Req with BindingChange attr. */
							if(msgIntegrityDetected == 1)
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
							else
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, NULL, procUdpConnReq);

#ifdef _CWMP_WITH_SSL_
							if (stunMsg.hasErrorCode && stunMsg.errorCode.errorClass == 4 &&
								stunMsg.errorCode.number == 1) // Unauthorized
							{
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);

								if (stunMsg.hasErrorCode)
								{
									tr098_printf("Unauthorized Username/Password (%d-%d)",
										         stunMsg.errorCode.errorClass,
										         stunMsg.errorCode.number);
									
									stunState = STUN_BINDING_REQ;
									close(priFd);
									break;
								}
							}
#endif
						}
					}


					if (!natDetected)
					{
						tr098_trace();
						gettimeofday(&now, NULL);
						outtime.tv_sec = now.tv_sec + stunPeriod;
						outtime.tv_nsec = now.tv_usec * 1000;
						pthread_cond_timedwait(&cond, &mutex, &outtime);
						//sleep(stunPeriod);
						break;
					}
				}
				else
				{
					tr098_trace();
					gettimeofday(&now, NULL);
					outtime.tv_sec = now.tv_sec + stunPeriod;
					outtime.tv_nsec = now.tv_usec * 1000;
					pthread_cond_timedwait(&cond, &mutex, &outtime);
					//sleep(stunPeriod);
					break;
				}

				tr098_printf("stunPeriod (%d)", stunPeriod);

				gettimeofday(&now, NULL);
				outtime.tv_sec = now.tv_sec + stunPeriod;
				outtime.tv_nsec = now.tv_usec * 1000;
				pthread_cond_timedwait(&cond, &mutex, &outtime);
				//sleep(stunPeriod);

				memset(&stunMsg, 0, sizeof(StunMessage));

				secFd = openPort(stunMappedAddr.port+1, 0);

				if(msgIntegrityDetected == 1)
					stunTest_tr111_send(secFd, &stunServer, 1, 1, 0, 0, 1, &stunMappedAddr, stunUsername, stunPassword);
				else
					stunTest_tr111_send(secFd, &stunServer, 1, 1, 0, 0, 1, &stunMappedAddr, stunUsername, NULL);

				stunTest_tr111_recv(priFd, 1, &stunMsg, procUdpConnReq);

				close(secFd);
				secFd = -1;

				if (stunMsg.hasMappedAddress) // not timeout yet
				{
					tr098_printf("not timeout yet");

					stunState = STUN_BINDING_MAINTAIN;
				}
				else // already timeout
				{
					tr098_printf("already timeout");

					close(priFd);
					priFd = -1;
					
					stunMaxPeriod = stunPeriod;
					stunState = STUN_BINDING_REQ;
				}
				break;

			case STUN_BINDING_CHANGE:
				mib_get(MIB_CWMP_STUN_SERVER_ADDR, stunServerAddr);
				mib_get(MIB_CWMP_STUN_SERVER_PORT, &stunServerPort);

				tr098_printf("stunServerAddr (%s)", stunServerAddr);
				tr098_printf("stunServerPort (%d)", stunServerPort);

				memset(&stunServer, 0, sizeof(StunAddress4));

				if (strlen(stunServerAddr) == 0)
				{
					stun_getHost(host, acsUrl);
				}
				else
				{
					stun_getHost(host, stunServerAddr);
				}

				tr098_printf("host %s", host);
					
				if (stun_getHostByName(host, &in) == -1)
					goto stun_err;
				
				stunServer.addr = (UInt32)in.s_addr;

				if (stunServerPort == 0)
				{
					stunServer.port = STUN_PORT; // use default stun port number
				}
				else
				{
					stunServer.port = stunServerPort;
				}

				//in.s_addr = htonl(stunServer.addr);
				in.s_addr = stunServer.addr;
				tr098_printf("stun server: %s:%d", inet_ntoa(in), stunServer.port);
				sprintf(checkStunServerAddrAndPort, "%s:%d", inet_ntoa(in), stunServer.port);

				if (strcmp(checkStunServerAddrAndPort, OldStunServerAddrAndPort) != 0)
				{
					close(priFd);
					priFd = -1;
					tr098_printf("Stun Server Address or port changed!!!");
					strcpy(OldStunServerAddrAndPort, checkStunServerAddrAndPort);

					priFd = openPort(0, 0);

					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, NULL, procUdpConnReq);
					if (stunMsg.hasErrorCode && stunMsg.errorCode.errorClass == 4 &&
								stunMsg.errorCode.number == 1) // Unauthorized
					{
						msgIntegrityDetected = 1;
						stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);

						if (stunMsg.hasErrorCode)
						{
							tr098_printf("Unauthorized Username/Password");
							close(priFd);
							priFd = -1;
							break;
						}
					}
					else
					{
						msgIntegrityDetected = 0;
					}

					if (stunMsg.hasMappedAddress)
					{
						s = openPort(11000, stunMsg.mappedAddress.ipv4.addr);

						if (s != INVALID_SOCKET) // not behind NAT
						{
							close(s);
							s = -1;
							natDetected = 0;
						}
						else
						{
							natDetected = 1;
						}

						stunMappedAddr.addr = stunMsg.mappedAddress.ipv4.addr;
						stunMappedAddr.port = stunMsg.mappedAddress.ipv4.port;

						in.s_addr = htonl(stunMsg.mappedAddress.ipv4.addr);
						sprintf(udpConnReqAddr, "%s:%d", inet_ntoa(in),
							stunMsg.mappedAddress.ipv4.port);

						mib_set(MIB_CWMP_UDP_CONN_REQ_ADDR, udpConnReqAddr);
						mib_set(MIB_CWMP_NAT_DETECTED, &natDetected);

						tr098_printf("isNAT: %d, UDPConnReqAddr: %s", natDetected, udpConnReqAddr);

						if (natDetected)
						{
							if (stunMaxPeriod == stunMinPeriod)
							{
								stunPeriod = stunMinPeriod;
								stunState = STUN_BINDING_MAINTAIN;
							}
							else
							{
								stunPeriod = (stunMinPeriod + stunMaxPeriod) / 2;
								if(stunPeriod == stunMinPeriod || stunPeriod == stunMaxPeriod)
									stunState = STUN_BINDING_MAINTAIN;
								else
									stunState = STUN_BINDING_TIMEOUT_DISCOVERY;
							}

							tr098_printf("stunState (%d) stunPeriod (%d) "
								"stunMinPeriod (%d) stunMaxPeriod (%d)",
								stunState, stunPeriod, stunMinPeriod, stunMaxPeriod);
						}
						else
						{
							tr098_trace();
							close(priFd);
							priFd = -1;
							stunState = STUN_BINDING_REQ;
							gettimeofday(&now, NULL);
							outtime.tv_sec = now.tv_sec + stunMaxPeriod;
							outtime.tv_nsec = now.tv_usec * 1000;
							pthread_cond_timedwait(&cond, &mutex, &outtime);
							//sleep(stunMaxPeriod);
						}
					}
					else
					{
						tr098_trace();
						close(priFd);
						priFd = -1;
						stunState = STUN_BINDING_REQ;
						gettimeofday(&now, NULL);
						outtime.tv_sec = now.tv_sec + stunMaxPeriod;
						outtime.tv_nsec = now.tv_usec * 1000;
						pthread_cond_timedwait(&cond, &mutex, &outtime);
						//sleep(stunMaxPeriod);
					}
				}
				else
				{
					tr098_trace();
					stunState = OldStunState;
				}
				break;

			case STUN_CHANGE_OTHER_PARAM:
				goto stun_reread;

			default:
				break;
		}
	}

stun_err:

	if(priFd != -1)
		close(priFd);
	if(secFd != -1)
		close(secFd);
	if(s != -1)
		close(s);

	tr098_printf("stun stopped!!!");

	stunThread = 0;
	pthread_mutex_unlock(&mutex);
}
#if 0
static void *stun_thread(void *arg)
{
	StunAddress4 stunServer, stunMappedAddr;
	StunMessage stunMsg;
	Socket priFd, secFd, s;
	struct in_addr in;
	char checkAddr[CWMP_UDP_CONN_REQ_ADDR_LEN+1];
	char host[CWMP_ACS_URL_LEN+1];
	unsigned int checkNat;
	unsigned int stunPeriod;

	tr098_printf("stun started!!!");

	stunState = STUN_BINDING_REQ;

	mib_get(MIB_CWMP_STUN_SERVER_ADDR, stunServerAddr);
	mib_get(MIB_CWMP_STUN_SERVER_PORT, &stunServerPort);
	mib_get(MIB_CWMP_STUN_USERNAME, stunUsername);
	mib_get(MIB_CWMP_STUN_PASSWORD, stunPassword);
	mib_get(MIB_CWMP_STUN_MAX_KEEP_ALIVE_PERIOD, &stunMaxPeriod);
	mib_get(MIB_CWMP_STUN_MIN_KEEP_ALIVE_PERIOD, &stunMinPeriod);

	mib_get(MIB_CWMP_ACS_URL, acsUrl);

	tr098_printf("stunServerAddr (%s)", stunServerAddr);
	tr098_printf("stunServerPort (%d)", stunServerPort);
	tr098_printf("stunUsername (%s)", stunUsername);
	tr098_printf("stunPassword (%s)", stunPassword);
	tr098_printf("stunMaxPeriod (%d)", stunMaxPeriod);
	tr098_printf("stunMinPeriod (%d)", stunMinPeriod);
	tr098_printf("acsUrl (%s)", acsUrl);

	memset(&stunServer, 0, sizeof(StunAddress4));

	if (strlen(stunServerAddr) == 0)
	{
		stun_getHost(host, acsUrl);
	}
	else
	{
		stun_getHost(host, stunServerAddr);
	}

	tr098_printf("host %s", host);
		
	if (stun_getHostByName(host, &in) == -1)
		goto stun_err;
	
	stunServer.addr = (UInt32)in.s_addr;

	if (stunServerPort == 0)
	{
		stunServer.port = STUN_PORT; // use default stun port number
	}
	else
	{
		stunServer.port = stunServerPort;
	}

	in.s_addr = htonl(stunServer.addr);
	tr098_printf("stun server: %s:%d", inet_ntoa(in), stunServer.port);
	
	while (!stunStop)
	{
		tr098_printf("stunState (%d)", stunState);

		memset(&stunMsg, 0, sizeof(StunMessage));
		
		switch (stunState)
		{
			case STUN_BINDING_REQ:
				tr098_trace();

				priFd = openPort(0, 0);

				stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 0, 0, NULL, stunUsername, NULL, procUdpConnReq);
				if (stunMsg.hasErrorCode && stunMsg.errorCode.errorClass == 4 &&
							stunMsg.errorCode.number == 1) // Unauthorized
				{
					msgIntegrityDetected = 1;
					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);

					if (stunMsg.hasErrorCode)
					{
						tr098_printf("Unauthorized Username/Password");
						close(priFd);
						break;
					}
				}
				else
				{
					msgIntegrityDetected = 0;
				}

				if (stunMsg.hasMappedAddress)
				{
					s = openPort(11000, stunMsg.mappedAddress.ipv4.addr);

					if (s != INVALID_SOCKET) // not behind NAT
					{
						close(s);
						natDetected = 0;
					}
					else
					{
						natDetected = 1;
					}

					stunMappedAddr.addr = stunMsg.mappedAddress.ipv4.addr;
					stunMappedAddr.port = stunMsg.mappedAddress.ipv4.port;

					in.s_addr = htonl(stunMsg.mappedAddress.ipv4.addr);
					sprintf(udpConnReqAddr, "%s:%d", inet_ntoa(in),
						stunMsg.mappedAddress.ipv4.port);

					mib_set(MIB_CWMP_UDP_CONN_REQ_ADDR, udpConnReqAddr);
					mib_set(MIB_CWMP_NAT_DETECTED, &natDetected);

					tr098_printf("isNAT: %d, UDPConnReqAddr: %s", natDetected, udpConnReqAddr);

					if (natDetected)
					{
						/* Binding Req with BindingChange attr. */
						if(msgIntegrityDetected == 1)
							stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
						else
							stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, NULL, procUdpConnReq);

#if 0//def _CWMP_WITH_SSL_
						if (stunMsg.hasErrorCode && stunMsg.errorCode.errorClass == 4 &&
							stunMsg.errorCode.number == 1) // Unauthorized
						{
							stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);

							if (stunMsg.hasErrorCode)
							{
								tr098_printf("Unauthorized Username/Password");
								close(priFd);
								break;
							}
						}
#endif

						if (stunMaxPeriod == stunMinPeriod)
						{
							stunPeriod = stunMinPeriod;
							stunState = STUN_BINDING_MAINTAIN;
						}
						else
						{
							stunPeriod = (stunMinPeriod + stunMaxPeriod) / 2;
							if(stunPeriod == stunMinPeriod || stunPeriod == stunMaxPeriod)
								stunState = STUN_BINDING_MAINTAIN;
							else
								stunState = STUN_BINDING_TIMEOUT_DISCOVERY;
						}

						tr098_printf("stunState (%d) stunPeriod (%d) "
							"stunMinPeriod (%d) stunMaxPeriod (%d)",
							stunState, stunPeriod, stunMinPeriod, stunMaxPeriod);
					}
					else
					{
						tr098_trace();
						close(priFd);
						sleep(stunMaxPeriod);
					}
				}
				else
				{
					tr098_trace();
					close(priFd);
					sleep(stunMaxPeriod);
				}
				break;

			case STUN_BINDING_MAINTAIN:
				sleep(stunPeriod);
				
				/* jump to STUN_BINDING_REQ if local IP changed */

				if(msgIntegrityDetected == 1)
					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 0, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
				else
					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 0, 0, NULL, stunUsername, NULL, procUdpConnReq);

				if (stunMsg.hasMappedAddress)
				{
					s = openPort(11000, stunMsg.mappedAddress.ipv4.addr);

					if (s != INVALID_SOCKET) // not behind NAT
					{
						close(s);
						checkNat = 0;
					}
					else
					{
						checkNat = 1;
					}

					in.s_addr = htonl(stunMsg.mappedAddress.ipv4.addr);
					sprintf(checkAddr, "%s:%d", inet_ntoa(in),
						stunMsg.mappedAddress.ipv4.port);

					tr098_printf("isNAT: %d, UDPConnReqAddr: %s", checkNat, checkAddr);

					if (checkNat != natDetected)
					{
						tr098_printf("natDetected changed!!!");
						mib_set(MIB_CWMP_NAT_DETECTED, &checkNat);
						natDetected = checkNat;
					}

					if (strcmp(checkAddr, udpConnReqAddr) != 0)
					{
						tr098_printf("UDPConnReqAddr changed!!!");
						mib_set(MIB_CWMP_UDP_CONN_REQ_ADDR, checkAddr);
						strcpy(udpConnReqAddr, checkAddr);

						/* do sometheing according to G.2.1.3 */
						if (natDetected)
						{
							/* Binding Req with BindingChange attr. */
							if(msgIntegrityDetected == 1)
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
							else
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, NULL, procUdpConnReq);

#if 0//def _CWMP_WITH_SSL_
							if (stunMsg.hasErrorCode && stunMsg.errorCode.errorClass == 4 &&
								stunMsg.errorCode.number == 1) // Unauthorized
							{
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);

								if (stunMsg.hasErrorCode)
								{
									tr098_printf("Unauthorized Username/Password");
									stunState = STUN_BINDING_REQ;
									close(priFd);
									break;
								}
							}
#endif
						}
					}
				}
				else
				{
					tr098_trace();
					stunState = STUN_BINDING_REQ;
					close(priFd);
					sleep(stunMaxPeriod);
				}
				break;

			case STUN_BINDING_TIMEOUT_DISCOVERY:
				sleep(stunPeriod);

				secFd = openPort(stunMappedAddr.port+1, 0);

				if(msgIntegrityDetected == 1)
					stunTest_tr111_send(secFd, &stunServer, 1, 1, 0, 0, 1, &stunMappedAddr, stunUsername, stunPassword);
				else
					stunTest_tr111_send(secFd, &stunServer, 1, 1, 0, 0, 1, &stunMappedAddr, stunUsername, NULL);

				stunTest_tr111_recv(priFd, 1, &stunMsg);

				if (stunMsg.hasMappedAddress) // not timeout yet
				{
					tr098_printf("not timeout yet, stunPeriod (%d)", stunPeriod);
					//stunPeriod *= 2;
					stunMinPeriod = stunPeriod;
					stunPeriod = (stunMinPeriod + stunMaxPeriod)/2;
					if(stunPeriod == stunMinPeriod || stunPeriod == stunMaxPeriod)
						stunState = STUN_BINDING_MAINTAIN;
					tr098_printf("stunPeriod = (%d)", stunPeriod);
				}
				else // already timeout
				{
					tr098_printf("already timeout, stunPeriod (%d)", stunPeriod);
					//stunPeriod /= 2;
					stunMaxPeriod = stunPeriod;
					stunPeriod = (stunMinPeriod + stunMaxPeriod)/2;
					tr098_printf("stunPeriod = (%d)", stunPeriod);
					stunState = STUN_BINDING_TIMEOUT_MONITORING;
				}

				close(secFd);
				break;

			case STUN_BINDING_TIMEOUT_MONITORING:
				if(msgIntegrityDetected == 1)
					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 0, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
				else
					stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 0, 0, NULL, stunUsername, NULL, procUdpConnReq);

				if (stunMsg.hasMappedAddress)
				{
					s = openPort(11000, stunMsg.mappedAddress.ipv4.addr);

					if (s != INVALID_SOCKET) // not behind NAT
					{
						close(s);
						checkNat = 0;
					}
					else
					{
						checkNat = 1;
					}

					stunMappedAddr.addr = stunMsg.mappedAddress.ipv4.addr;
					stunMappedAddr.port = stunMsg.mappedAddress.ipv4.port;

					in.s_addr = htonl(stunMsg.mappedAddress.ipv4.addr);
					sprintf(checkAddr, "%s:%d", inet_ntoa(in),
						stunMsg.mappedAddress.ipv4.port);

					tr098_printf("isNAT: %d, UDPConnReqAddr: %s", checkNat, checkAddr);

					if (checkNat != natDetected)
					{
						tr098_printf("natDetected changed!!!");
						mib_set(MIB_CWMP_NAT_DETECTED, &checkNat);
						natDetected = checkNat;
					}

					if (strcmp(checkAddr, udpConnReqAddr) != 0)
					{
						tr098_printf("UDPConnReqAddr changed!!!");
						mib_set(MIB_CWMP_UDP_CONN_REQ_ADDR, checkAddr);
						strcpy(udpConnReqAddr, checkAddr);

						/* do sometheing according to G.2.1.3 */
						if (natDetected)
						{
							/* Binding Req with BindingChange attr. */
							if(msgIntegrityDetected == 1)
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);
							else
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, NULL, procUdpConnReq);

#if 0//def _CWMP_WITH_SSL_
							if (stunMsg.hasErrorCode && stunMsg.errorCode.errorClass == 4 &&
								stunMsg.errorCode.number == 1) // Unauthorized
							{
								stunTest_tr111(priFd, &stunServer, 1, 1, &stunMsg, 1, 1, 0, NULL, stunUsername, stunPassword, procUdpConnReq);

								if (stunMsg.hasErrorCode)
								{
									tr098_printf("Unauthorized Username/Password (%d-%d)",
										         stunMsg.errorCode.errorClass,
										         stunMsg.errorCode.number);
									
									stunState = STUN_BINDING_REQ;
									close(priFd);
									break;
								}
							}
#endif
						}
					}


					if (!natDetected)
					{
						tr098_trace();
						sleep(stunPeriod);
						break;
					}
				}
				else
				{
					tr098_trace();
					sleep(stunPeriod);
					break;
				}

				tr098_printf("stunPeriod (%d)", stunPeriod);

				sleep(stunPeriod);

				memset(&stunMsg, 0, sizeof(StunMessage));

				secFd = openPort(stunMappedAddr.port+1, 0);

				if(msgIntegrityDetected == 1)
					stunTest_tr111_send(secFd, &stunServer, 1, 1, 0, 0, 1, &stunMappedAddr, stunUsername, stunPassword);
				else
					stunTest_tr111_send(secFd, &stunServer, 1, 1, 0, 0, 1, &stunMappedAddr, stunUsername, NULL);

				stunTest_tr111_recv(priFd, 1, &stunMsg);

				close(secFd);

				if (stunMsg.hasMappedAddress) // not timeout yet
				{
					tr098_printf("not timeout yet");

					stunState = STUN_BINDING_MAINTAIN;
				}
				else // already timeout
				{
					tr098_printf("already timeout");

					close(priFd);
					
					stunMaxPeriod = stunPeriod;
					stunState = STUN_BINDING_REQ;
				}
				break;

			default:
				break;
		}
	}

stun_err:
	
	close(priFd);
	close(secFd);
	close(s);

	tr098_printf("stun stopped!!!");

	stunThread = 0;
}
#endif

#ifndef TR181_SUPPORT
void cwmpStartStun()
{
	pthread_t stun_pid;
	
	tr098_trace();
	
	if (stunThread)
	{
		tr098_printf("stun already in progress!!!");
		return;
	}

	pthread_mutex_init(&mutex, NULL);
	pthread_cond_init(&cond, NULL);

	stunThread = 1;
	stunStop = 0;
	if( pthread_create( &stun_pid, NULL, stun_thread, 0 ) != 0 )
	{
		tr098_printf("stun thread create fail!!!");
		stunThread = 0;
		return;
	}
	
	pthread_detach(stun_pid);
}

void cwmpStopStun()
{
	tr098_trace();

	pthread_mutex_lock(&mutex);
	stunStop = 1;
	pthread_cond_signal(&cond);
	pthread_mutex_unlock(&mutex);
	
}
#endif
#endif

void MgmtSrvSetParamKey(const char *key) {
/*star:20091228 START add for store parameterkey*/
#if 1
	unsigned char gParameterKey[32+1];
	gParameterKey[0]='\0';
	if (key)
	{
		strncpy(gParameterKey, key, sizeof(gParameterKey) -1);
		gParameterKey[sizeof(gParameterKey) -1]=0;
	}
	mib_set(MIB_CWMP_PARAMETERKEY,gParameterKey);
#else
	gParameterKey[0]='\0';
	if (key)
	{
		strncpy(gParameterKey, key, sizeof(gParameterKey) -1);
		gParameterKey[sizeof(gParameterKey) -1]=0;
	}
#endif
/*star:20091228 END*/
}

int getMngmntServer(char *name, struct CWMP_LEAF *entity, int *type, void **data)
{
	//char	*lastname = entity->name;
	unsigned char buf[256+1]={0};
	unsigned char ch=0;
	unsigned int  in=0;
	int           in1=0;
	
	if( (name==NULL) || (type==NULL) || (data==NULL) || (entity==NULL)) 
		return -1;

	*type = entity->info->type;
	*data = NULL;
	switch(getIndexOf(tManagementServerLeaf, entity->info->name)) {
	case EN_ENABLE_CWMP:
		CONFIG_GET(MIB_CWMP_ENABLED, &in);
		*data = booldup(in);
		break;
	case EN_URL: //URL
		CONFIG_GET(MIB_CWMP_ACS_URL, buf);
		*data = strdup( buf );
		break;
	case EN_USERNAME: //Username
//	#if DEBUG
		CONFIG_GET(MIB_CWMP_ACS_USERNAME, buf);
		*data = strdup(buf);
//	#else
//		*data = strdup("");
//	#endif
		break;
	case EN_PASSWORD: // Password
	#if DEBUG
		CONFIG_GET(MIB_CWMP_ACS_PASSWORD, buf);
		*data = strdup(buf);
	#else
		*data = strdup("");
	#endif
		break;
	case EN_PERIODIC_INFORM_ENABLE: // #PeriodicInformEnable
		CONFIG_GET(MIB_CWMP_INFORM_ENABLE, &in);
		*data = booldup(in);
		break;	
	case EN_PERIODIC_INTERVAL: // PeriodicInformInterval
		CONFIG_GET(MIB_CWMP_INFORM_INTERVAL, &in);
		*data = uintdup(in);
		break;	
	case EN_PERIODIC_TIME: // PeriodicInformTime
		CONFIG_GET(MIB_CWMP_INFORM_TIME, &in);
		*data = timedup(in);
		break;	
	case EN_PARAMETER_KEY: // ParameterKey
		{
/*star:20091228 START add for store parameterkey*/
			unsigned char gParameterKey[32+1];
			CONFIG_GET(MIB_CWMP_PARAMETERKEY,gParameterKey);
/*star:20091228 END*/
		*data = strdup(gParameterKey);
		break;	
		}
	case EN_CONNREQ_URL: // ConnectionRequestURL
		if (MgmtSrvGetConReqURL(buf, 256))			
			*data = strdup(buf);
		else
			*data = strdup("");
		break;	
	case EN_CONNREQ_USERNAME: // ConnectionRequestUsername
		CONFIG_GET(MIB_CWMP_CONREQ_USERNAME, buf);
		*data = strdup(buf);
		break;	
	case EN_CONNREQ_PASSWORD: // ConnectionRequestPassword
//	#if DEBUG
		CONFIG_GET(MIB_CWMP_CONREQ_PASSWORD, buf);
		*data = strdup(buf);
//	#else
//		*data = strdup("");
//	#endif
		break;	
	case EN_UPGRADE_MANAGED: // UpgradesManaged
		CONFIG_GET(MIB_CWMP_ACS_UPGRADESMANAGED, &ch);
		*data = booldup(ch);
		break;
	case EN_KICKURL:
		CONFIG_GET(MIB_CWMP_ACS_KICKURL, buf);
		*data = strdup(buf);
		break;	
	case EN_DOWNLOADURL:
		CONFIG_GET(MIB_CWMP_ACS_DOWNLOADURL, buf);
		*data = strdup(buf);
		break;		
	case EN_DEFAULT_ACTIVE_NORTIFICATION_THROTTLE:
		CONFIG_GET(MIB_CWMP_DEF_ACT_NOTIF_THROTTLE, &in);
		*data = uintdup(in);
		break;
#ifdef TR069_ANNEX_G
	case EN_UDPConnectionRequestAddress:
		CONFIG_GET(MIB_CWMP_UDP_CONN_REQ_ADDR, buf);
		*data = strdup(buf);
		break;
		
	case EN_STUNEnable:
		CONFIG_GET(MIB_CWMP_STUN_EN, &in);
		*data = booldup(in);
		break;
		
	case EN_STUNServerAddress:
		CONFIG_GET(MIB_CWMP_STUN_SERVER_ADDR, buf);
		*data = strdup(buf);
		break;
		
	case EN_STUNServerPort:
		CONFIG_GET(MIB_CWMP_STUN_SERVER_PORT, &in);
		*data = uintdup(in);
		break;
		
	case EN_STUNUsername:
		CONFIG_GET(MIB_CWMP_STUN_USERNAME, buf);
		*data = strdup(buf);
		break;
		
	case EN_STUNPassword:
		CONFIG_GET(MIB_CWMP_STUN_PASSWORD, buf);
		*data = strdup(buf);
		break;
		
	case EN_STUNMaximumKeepAlivePeriod:
		CONFIG_GET(MIB_CWMP_STUN_MAX_KEEP_ALIVE_PERIOD, &in1);
		*data = intdup(in1);
		break;
		
	case EN_STUNMinimumKeepAlivePeriod:
		CONFIG_GET(MIB_CWMP_STUN_MIN_KEEP_ALIVE_PERIOD, &in);
		*data = uintdup(in);
		break;
		
	case EN_NATDetected:
		CONFIG_GET(MIB_CWMP_NAT_DETECTED, &in);
		*data = uintdup(in);
		break;
#endif
#ifdef _TR_111_PRMT_
	case EN_MANAGEABLEDEVICENUMBER:
	{
/*star:20100127 START add to update gDeviceNumber, used for notification list check*/
		FILE *fp;
		int count=0;
		fp=fopen( TR111_DEVICEFILE, "r" );
		
		while( fp && fgets( buf,160,fp ) )
		{
			char *p;
			
			p = strtok( buf, " \n\r" );
			if( p && atoi(p)>0 )
			{
				count++;
			}
		}
		if(fp) fclose(fp);
		gDeviceNumber = count;
/*star:20100127 END*/		
		*data = uintdup(gDeviceNumber);
	}
		break;
	case EN_MANAGEABLE_DEVICE_NORTIFICATION_LIMIT:
		CONFIG_GET(MIB_CWMP_MANAGE_DEV_NOTIF_LIMIT, &in);
		*data = uintdup(in);
		break;
#endif
	default:
		return ERR_9005;
				
	}

	return 0;

}

extern void parse_xml_escaped_str(char *out, char *in);
int setMngmntServer(char *name, struct CWMP_LEAF *entity, int type, void *data)
{
	char	*lastname = entity->info->name;
	char 	*buf=data;
	char 	tmp[256+1]={0};
	int  	len=0;	
	unsigned int *pNum;
	unsigned char byte;
	unsigned int iVal;
	unsigned int flag;
	int          *pInt;
	
	if( (name==NULL) || (entity==NULL)) return -1;
	if( entity->info->type!=type ) return ERR_9006;

	printf("name is <%s>\n", lastname);

	switch(getIndexOf(tManagementServerLeaf, entity->info->name)) {
	case EN_ENABLE_CWMP:
		pNum = (unsigned int *)data;
		CHECK_PARAM_NUM(*pNum, 0, 1);
		iVal = (*pNum == 0) ? 0 : 1;
		CONFIG_SET(MIB_CWMP_ENABLED, &iVal);	

		if (iVal == 0)
		{
			//int cwmppid = 0;
			
			CONFIG_GET(MIB_CWMP_FLAG, &flag);
			flag = flag & (~CWMP_FLAG_AUTORUN);
			CONFIG_SET(MIB_CWMP_FLAG, &flag);	

			//cwmppid = getPid((char*)CWMPPID);
			//printf("\ncwmppid=%d\n",cwmppid);
			//if(cwmppid > 0)
			//	kill(cwmppid, 15);

			return 1;
		}
		break;
	case EN_URL: //URL
		CHECK_PARAM_STR(buf, 0, 256+1);
		char tmpstr[256+1] = {0};
        CONFIG_GET(MIB_CWMP_ACS_URL, tmpstr);
        CONFIG_SET(MIB_CWMP_ACS_URL_OLD, tmpstr);
		CONFIG_SET(MIB_CWMP_ACS_URL, buf);
		//break;
		return 1; // reboot to take effect
	case EN_USERNAME: //Username
		CHECK_PARAM_STR(buf, 0, 256+1);
		parse_xml_escaped_str(tmp, buf);
		CONFIG_SET(MIB_CWMP_ACS_USERNAME, tmp);
		//break;
		return 1; // reboot to take effect
	case EN_PASSWORD: // Password
		CHECK_PARAM_STR(buf, 0, 256+1);
		parse_xml_escaped_str(tmp, buf);
		CONFIG_SET(MIB_CWMP_ACS_PASSWORD, tmp);
		//break;
		return 1; // reboot to take effect
	case EN_PERIODIC_INFORM_ENABLE: // #PeriodicInformEnable
		pNum = (unsigned int *)data;
		CHECK_PARAM_NUM(*pNum, 0, 1);
		iVal = (*pNum == 0) ? 0 : 1;
		CONFIG_SET(MIB_CWMP_INFORM_ENABLE, &iVal);	
/*star:20100112 START move to port_update_userdata()*/
		//cwmpMgmtSrvInformInterval();
/*star:20100112 END*/
		break;	
	case EN_PERIODIC_INTERVAL: // PeriodicInformInterval
		pNum = (unsigned int *)data;
		if (*pNum < 1) return ERR_9007;		
		CONFIG_SET(MIB_CWMP_INFORM_INTERVAL, pNum);
		reInformIntervalFlag = 1;
/*star:20100112 START move to port_update_userdata()*/
		//cwmpMgmtSrvInformInterval();
/*star:20100112 END*/
		break;	
	case EN_PERIODIC_TIME: // PeriodicInformTime
		pNum = (unsigned int *)buf;
		printf("time is %u\n", *pNum);
		CONFIG_SET(MIB_CWMP_INFORM_TIME, buf);
/*star:20100112 START move to port_update_userdata()*/
		//cwmpMgmtSrvInformInterval();
/*star:20100112 END*/
		break;	

	case EN_CONNREQ_USERNAME: // ConnectionRequestUsername
		CHECK_PARAM_STR(buf, 0, 256+1);
		parse_xml_escaped_str(tmp, buf);
		CONFIG_SET(MIB_CWMP_CONREQ_USERNAME, tmp);
		//break;
		return 1; // reboot to take effect
	case EN_CONNREQ_PASSWORD: // ConnectionRequestPassword
		CHECK_PARAM_STR(buf, 0, 256+1);
		parse_xml_escaped_str(tmp, buf);
		CONFIG_SET(MIB_CWMP_CONREQ_PASSWORD, tmp);
		//break;
		return 1; // reboot to take effect
	case EN_UPGRADE_MANAGED: // UpgradesManaged
		pNum = (unsigned int *)data;
		CHECK_PARAM_NUM(*pNum, 0, 1);
		byte = (*pNum == 0) ? 0 : 1;
		CONFIG_SET(MIB_CWMP_ACS_UPGRADESMANAGED, &byte);	
		break;
	case EN_DEFAULT_ACTIVE_NORTIFICATION_THROTTLE:
		pNum = (unsigned int *)data;
		iVal = (*pNum );
		CONFIG_SET(MIB_CWMP_DEF_ACT_NOTIF_THROTTLE, &iVal);
		break;
#ifdef TR069_ANNEX_G
	case EN_STUNEnable:
		pNum = (unsigned int *)data;
		CHECK_PARAM_NUM(*pNum, 0, 1);
		iVal = (*pNum == 0) ? 0 : 1;
		CONFIG_SET(MIB_CWMP_STUN_EN, &iVal);	

		if (iVal)
			cwmpStartStun();
		else
			cwmpStopStun();
		break;
		
	case EN_STUNServerAddress:
		if(stunState != STUN_BINDING_CHANGE)
		{
			CHECK_PARAM_STR(buf, 0, CWMP_STUN_SERVER_ADDR_LEN+1);
			CONFIG_SET(MIB_CWMP_STUN_SERVER_ADDR, buf);
			pthread_mutex_lock(&mutex);
			if(stunState != STUN_BINDING_REQ)
			{
				OldStunState = stunState;
				stunState = STUN_BINDING_CHANGE;
			}
			pthread_cond_signal(&cond);
			pthread_mutex_unlock(&mutex);
		}
		break;
		
	case EN_STUNServerPort:
		if(stunState != STUN_BINDING_CHANGE)
		{
			pNum = (unsigned int *)data;
			if ((*pNum < 0) || (*pNum > 65535)) return ERR_9007;		
			CONFIG_SET(MIB_CWMP_STUN_SERVER_PORT, pNum);
			pthread_mutex_lock(&mutex);
			if(stunState != STUN_BINDING_REQ)
			{
				OldStunState = stunState;
				stunState = STUN_BINDING_CHANGE;
			}
			pthread_cond_signal(&cond);
			pthread_mutex_unlock(&mutex);
		}
		break;
		
	case EN_STUNUsername:
		CHECK_PARAM_STR(buf, 0, CWMP_STUN_USERNAME_LEN+1);
		CONFIG_SET(MIB_CWMP_STUN_USERNAME, buf);
		pthread_mutex_lock(&mutex);
		stunState = STUN_CHANGE_OTHER_PARAM;
		pthread_cond_signal(&cond);
		pthread_mutex_unlock(&mutex);
		break;
		
	case EN_STUNPassword:
		CHECK_PARAM_STR(buf, 0, CWMP_STUN_PASSWORD_LEN+1);
		CONFIG_SET(MIB_CWMP_STUN_PASSWORD, buf);
		pthread_mutex_lock(&mutex);
		stunState = STUN_CHANGE_OTHER_PARAM;
		pthread_cond_signal(&cond);
		pthread_mutex_unlock(&mutex);
		break;
		
	case EN_STUNMaximumKeepAlivePeriod:
		pInt = (int *)data;
		if (*pInt < -1) return ERR_9007;	
		CONFIG_SET(MIB_CWMP_STUN_MAX_KEEP_ALIVE_PERIOD, pInt);
		pthread_mutex_lock(&mutex);
		stunState = STUN_CHANGE_OTHER_PARAM;
		pthread_cond_signal(&cond);
		pthread_mutex_unlock(&mutex);
		break;
		
	case EN_STUNMinimumKeepAlivePeriod:
		pNum = (unsigned int *)data;
		CONFIG_SET(MIB_CWMP_STUN_MIN_KEEP_ALIVE_PERIOD, pNum);
		pthread_mutex_lock(&mutex);
		stunState = STUN_CHANGE_OTHER_PARAM;
		pthread_cond_signal(&cond);
		pthread_mutex_unlock(&mutex);
		break;
#endif
#ifdef _TR_111_PRMT_
	case EN_MANAGEABLE_DEVICE_NORTIFICATION_LIMIT:
		pNum = (unsigned int *)data;
		iVal = (*pNum );
		CONFIG_SET(MIB_CWMP_MANAGE_DEV_NOTIF_LIMIT, &iVal);
		break;
#endif
	default:
		return ERR_9005;
				
	}

	return 0;
}


#ifdef _TR_111_PRMT_
extern unsigned int getInstNum( char *name, char *objname );
int getManageDeviceInfo( unsigned int num, char *sOUI, char *sSN, char *sClass );
int getManageDevEntity(char *name, struct CWMP_LEAF *entity, int *type, void **data)
{
	char		*lastname = entity->info->name;
	unsigned int	num=0;
	char		sOUI[7]="", sSN[65]="", sClass[65]="";
	
	if( (name==NULL) || (type==NULL) || (data==NULL) || (entity==NULL)) 
		return -1;

	num = getInstNum( name, "ManageableDevice" );
	if(num==0) return ERR_9005;
	getManageDeviceInfo( num, sOUI, sSN, sClass );

	*type = entity->info->type;
	*data = NULL;
	if( strcmp( lastname, "ManufacturerOUI" )==0 )
	{
		*data = strdup( sOUI );
	}else if( strcmp( lastname, "SerialNumber" )==0 )
	{
		*data = strdup( sSN );
	}else if( strcmp( lastname, "ProductClass" )==0 )
	{
		*data = strdup( sClass );
/*ping_zhang:20081217 START:patch from telefonica branch to support WT-107*/
#ifdef _PRMT_WT107_
	}else if( strcmp( lastname, "Host" )==0 )
	{
		extern unsigned int gDHCPTotalHosts;
		extern int updateDHCP();
		char hostStrHead[] = "InternetGatewayDevice.LANDevice.1.Hosts.Host.";
		char hostStr[1000];
		int i,len=0;
		
		updateDHCP();
		
		if(gDHCPTotalHosts>0)
		{
			for(i=0; i<gDHCPTotalHosts; i++)
			{
				if( len+strlen(hostStrHead)+16 > sizeof(hostStr) )
					break;

				if(i!=0)
					len += sprintf(hostStr+len,",");
				len += sprintf(hostStr+len,"%s%d",hostStrHead,i+1);
			}
			*data = strdup(hostStr);
		}
		else
			*data = strdup( "" );
#endif
/*ping_zhang:20081217 END*/
	}else{
		return ERR_9005;
	}
	
	return 0;
}

int objManageDevice(char *name, struct CWMP_LEAF *e, int type, void *data)
{
	struct CWMP_NODE *entity=(struct CWMP_NODE *)e;
	FILE *fp;
	char buf[160];

	//fprintf( stderr, "%s:action:%d: %s\n", __FUNCTION__, type, name);

	switch( type )
	{
	case eCWMP_tINITOBJ:
	     {
		int MaxInstNum=0,count=0;
		struct CWMP_LINKNODE **c = (struct CWMP_LINKNODE **)data;

		if( (name==NULL) || (entity==NULL) || (data==NULL) ) return -1;

		fp=fopen( TR111_DEVICEFILE, "r" );
		if(fp==NULL) return 0;
		
		while( fgets( buf,160,fp ) )
		{
			char *p;
			
			p = strtok( buf, " \n\r" );
			if( p && atoi(p)>0 )
			{
				if( MaxInstNum < atoi(p) )
					MaxInstNum = atoi(p);
				
				if( create_Object( c, tManageableDeviceObject, sizeof(tManageableDeviceObject), 1, atoi(p) ) < 0 )
					break;
				count++;
				//c = & (*c)->sibling;
			}
		}
		fclose(fp);
		gDeviceNumber = count;
		add_objectNum( name, MaxInstNum );
		return 0;
	     }
	case eCWMP_tUPDATEOBJ:	
	     {
	     	int count=0;
	     	struct CWMP_LINKNODE *old_table;
	
	     	old_table = (struct CWMP_LINKNODE *)entity->next;
	     	entity->next = NULL;

		fp=fopen( TR111_DEVICEFILE, "r" );
	     	while( fp && fgets( buf,160,fp ) )
	     	{
	     		struct CWMP_LINKNODE *remove_entity=NULL;
			char *p;
			
			p = strtok( buf, " \n\r" );
			if( p && atoi(p)>0 )
			{	
				remove_entity = remove_SiblingEntity( &old_table, atoi(p) );
				if( remove_entity!=NULL )
				{
					add_SiblingEntity( (struct CWMP_LINKNODE **)&entity->next, remove_entity );
				}else{ 
					unsigned int MaxInstNum;
					MaxInstNum = atoi(p);					
					add_Object( name, (struct CWMP_LINKNODE **)&entity->next,  tManageableDeviceObject, sizeof(tManageableDeviceObject), &MaxInstNum );
				}
				count++;
			}
	     	}
			
	     	if(fp) fclose(fp);

	     	gDeviceNumber = count;
	     	if( old_table )
	     		destroy_ParameterTable( (struct CWMP_NODE *)old_table );
	     	return 0;
	     }
	}
	
	return -1;
}



int getManageDeviceInfo( unsigned int num, char *sOUI, char *sSN, char *sClass )
{
	FILE *fp;
	int ret=-1;
	
	if( num<=0 || sOUI==NULL || sSN==NULL || sClass==NULL ) return ret;
	
	sOUI[0]=sSN[0]=sClass[0]=0;
	
	fp=fopen( TR111_DEVICEFILE, "r" );
	if(fp)
	{
		char buf[160], *p, *n;
		
		while( fgets( buf,160,fp ) )
		{
			p = strtok( buf, " \n\r" );
			n = strtok( NULL, "\n\r" );
			if( p && (atoi(p)==num) && n )
			{
				char *s1=NULL, *s2=NULL, *s3=NULL;
				
				s1 = strtok( n, "?\n\r" );
				s2 = strtok( NULL, "?\n\r" );
				s3 = strtok( NULL, "?\n\r" );
				if( s1 && s2 && s3 )
				{
					strncpy( sOUI, s1, 6 );
					sOUI[6]=0;
					strncpy( sClass, s2, 64 );
					sClass[64]=0;
					strncpy( sSN, s3, 64 );
					sSN[64]=0;
					ret = 0;
				}else if( s1 && s2 )
				{
					strncpy( sOUI, s1, 6 );
					sOUI[6]=0;
					sClass[0]=0;
					strncpy( sSN, s2, 64 );
					sSN[64]=0;
					ret = 0;
				}//else error
				break;
			}
		}
		
		fclose(fp);
	}
	return ret;
}
#endif
